package cn.xinfei.xdecision.engine.runner.metadata;

import cn.xinfei.xdecision.common.model.configcenter.rejectcode.model.RejectBranchCodeModel;
import cn.xinfei.xdecision.common.model.enginex.po.Engine;
import cn.xinfei.xdecision.common.model.enginex.po.EngineVersion;
import cn.xinfei.xdecision.common.model.enginex.vo.EngineVersionInOutVarsRespVo;
import cn.xinfei.xdecision.common.utils.util.JsonUtils;
import cn.xinfei.xdecision.common.vo.FieldInfoVo;
import cn.xinfei.xdecision.engine.runner.executor.NodeHandler;
import cn.xinfei.xdecision.thread.CustomerThreadPoolExecutor;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;


/**
 * 此类的触发,必须在nacos注册之前
 */
@Service
@Slf4j
@Data
public class MetadataProvider {

    public static MetadataProvider metadataProvider;

    @Autowired
    private DecisionFlowLoader decisionFlowLoader;

    @Autowired
    private FieldInfoLoader fieldInfoLoader;

    @Autowired
    private EngineInfoLoader engineInfoLoader;

    @Autowired
    private RejectCodeLoader rejectCodeLoader;

    @Autowired
    private EngineInOutFieldLoader engineInOutFieldLoader;

    @Autowired
    private MetadataProperties metadataProperties;

    /**
     * engineCode:nodeCode:engineNode
     */
    public static volatile Table<String, Long, Engine> engineAndVersionMapCache = HashBasedTable.create();
    /**
     * engineCode:nodeCode:node
     */
    public static volatile Table<String, String, NodeHandler> engineAndNodeMapCache = HashBasedTable.create();
    public static volatile Map<String, FieldInfoVo> fieldMapCache = new ConcurrentHashMap<>();
    public static volatile Map<String, EngineVersion> engineVersionMapCache = new ConcurrentHashMap<>();
    public static volatile Map<Long, RejectBranchCodeModel> rejectBranchCodeMapCache = new ConcurrentHashMap<>();
    /**
     * engineCode, versionNo, inOutVarList
     */
    public static volatile Table<String, Long, EngineVersionInOutVarsRespVo> inOutVarsCache = HashBasedTable.create();

    @Resource(name = "metadataCacheReloadThreadPool")
    public CustomerThreadPoolExecutor metadataCacheReloadThreadPool;


    @PostConstruct
    public void construct() {
        MetadataProvider.metadataProvider = this;
    }

    public void init() {
        /**
         * TODO shiyusen engineNode,field,strategyOut之间会存在依赖关系，所以他们之间的缓存更新应该实现一致性
         */
        log.info("=============reload metadata start==================");
        long start = System.currentTimeMillis();
        async(() -> {
            rejectBranchCodeMapCache = rejectCodeLoader.init(null);
        });

        sync(() -> {
            fieldMapCache = fieldInfoLoader.init(null);
        });

        sync(() -> {
            engineAndVersionMapCache = engineInfoLoader.init(null);
        });

        async(() -> {
            inOutVarsCache = engineInOutFieldLoader.init(engineAndVersionMapCache);
        });

        sync(() -> {
            engineVersionMapCache = decisionFlowLoader.init(engineAndVersionMapCache);
        });

        sync(() -> {
            engineAndNodeMapCache = decisionFlowLoader.init(engineVersionMapCache);
        });

        long end = System.currentTimeMillis();

        log.info("====================================reload metadata end,respond={}=======================================================", end - start);
    }

    private void async(XAction action) {
        metadataCacheReloadThreadPool.submit(new XCacheReloadThread(action));
    }

    private void sync(XAction action) {
        Future<?> engineAndNodeMapCacheFuture = metadataCacheReloadThreadPool.submit(new XCacheReloadThread(action));

        try {
            engineAndNodeMapCacheFuture.get();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public Table<String, Long, Engine> getEngineAndVersionMapCache() {
        return engineAndVersionMapCache;
    }

    public static Table<String, String, NodeHandler> getEngineAndNodeMapCache() {
        return engineAndNodeMapCache;
    }

    public Map<String, FieldInfoVo> getFieldInfo() {
        return fieldMapCache;
    }

    public static Map<String, EngineVersion> getEngineVersionMapCache() {
        return engineVersionMapCache;
    }

    public Map<Long, RejectBranchCodeModel> getRejectBranchCodeInfo() {
        return rejectBranchCodeMapCache;
    }


    public static Table<String, Long, EngineVersionInOutVarsRespVo> getInOutVarsCache() {
        return inOutVarsCache;
    }

    public EngineVersion getEngineVersion(String engineCode) {
        return engineVersionMapCache.get(engineCode);
    }

    public static EngineVersionInOutVarsRespVo getInOutVarsCache(String engineCode, Long versionNo) {
        EngineVersionInOutVarsRespVo engineVersionInOutVarsRespVo = inOutVarsCache.get(engineCode, versionNo);
        return engineVersionInOutVarsRespVo;
    }

    public Map<String, NodeHandler> getEngineNodeList(String engineCode) {
        Map<String, NodeHandler> row = null;
        try {
            row = engineAndNodeMapCache.row(engineCode);
        } catch (Exception e) {
            log.info("getEngineNode from cache fail,msg={},engineCode={},value={}", e.getMessage(), engineCode, JsonUtils.toJsonString(row), e);
        }
        if (row != null) {
//            log.info("getEngineNode from cache,key={},value={}", engineCode, row.size());
        } else {
            log.error("getEngineNode from cache,key={},value is null");
        }
        return row;
    }

    public Map<Long, Engine> getEngineList(String engineCode) {
        Map<Long, Engine> row = null;
        try {
            row = engineAndVersionMapCache.row(engineCode);
        } catch (Exception e) {
            log.info("getEngineList from cache fail,msg={},key={},value={}", e.getMessage(), engineCode, JsonUtils.toJsonString(row), e);
        }
        log.info("getEngineList from cache,key={}", engineCode);
        return row;
    }


    class XCacheReloadThread implements Runnable {

        XAction xAction;

        public XCacheReloadThread(XAction xAction) {
            this.xAction = xAction;
        }

        @Override
        public void run() {
            try {
                xAction.emit();
            } catch (Exception e) {
                log.error("metadata load error", e);
            }
        }
    }

    @FunctionalInterface
    public interface XAction {

        void emit();

    }

}

